package chamster.random;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import chamster.BaseHamster;
import chamster.actions.HamsterAction;
/**
 * Takes a map of actions that the hamster should execute
 * and provides methods to select randomly one of them
 * @author mark
 *
 * @param <HamsterType>
 */
public abstract class RandomStatelessActionMachine<HamsterType extends BaseHamster> {
	private final Map<HamsterAction<HamsterType>, Integer> actions;
	
	public RandomStatelessActionMachine(Map<HamsterAction<HamsterType>, Integer> actions) {
		this.actions = actions;
	}
	
	public HamsterAction<HamsterType> decide(HamsterType hamster) throws Exception {
		return this.selectAction(this.getApplicableActions(hamster));
	}
	
	protected HamsterAction<HamsterType> selectAction(Map<HamsterAction<HamsterType>, Integer> applicableActions) {
		int base = 0;
		for(HamsterAction<HamsterType> action : applicableActions.keySet()) {
			base = base + applicableActions.get(action);
		}
		Random r = new Random();
		HamsterAction<HamsterType> actionToExecute = null;
		double maxChance = 0;
		for(HamsterAction<HamsterType> action : applicableActions.keySet()) {
			double chance = r.nextDouble() * applicableActions.get(action) / base;
			if(chance > maxChance) {
				maxChance = chance;
				actionToExecute = action;
			}
		}
		return actionToExecute;
	}
	
	public HamsterAction<HamsterType> decideAndExecute(HamsterType hamster) throws Exception {
		HamsterAction<HamsterType> action = this.decide(hamster);
		action.run(hamster);
		this.actions.put(action, newChance(action, this.actions.get(action)));
		return action;
	}
	
	public Map<HamsterAction<HamsterType>, Integer> getApplicableActions(HamsterType hamster) throws Exception {
		HashMap<HamsterAction<HamsterType>, Integer> applicableActions = 
				new HashMap<HamsterAction<HamsterType>, Integer>();
		for(HamsterAction<HamsterType> action : this.actions.keySet()) {
			if(action.isApplicable(hamster)) {
				applicableActions.put(action, this.actions.get(action));
			}
		}
		return applicableActions;
	}
	/**
	 * Returns weather this machine has applicable actions for the given hamster
	 * @param hamster The hamster
	 * @return Whether applicable actions are avail. for the given hamster
	 * @throws Exception
	 */
	public boolean hasApplicableActions(HamsterType hamster) throws Exception {
		return !this.getApplicableActions(hamster).isEmpty();
	}
	
	protected abstract int newChance(HamsterAction<HamsterType> executedAction, int oldChance);
}
